﻿namespace CRM2011InternalEmail.Workflow
{
    using Entities;
    using Microsoft.Crm.Sdk.Messages;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Query;
    using Microsoft.Xrm.Sdk.Workflow;
    using System;
    using System.Activities;
    using System.Collections.Generic;
    using System.ServiceModel;

    public sealed class EmailSecurityRole : CodeActivity
    {
        List<ActivityParty> toList;

        /// <summary>
        /// Executes the workflow activity.
        /// </summary>
        /// <param name="executionContext">The execution context.</param>
        protected override void Execute(CodeActivityContext executionContext)
        {
            // Create the tracing service
            ITracingService tracingService = executionContext.GetExtension<ITracingService>();

            if (tracingService == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
            }

            tracingService.Trace("Entered EmailSecurityRole.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
                executionContext.ActivityInstanceId, executionContext.WorkflowInstanceId);

            // Create the context
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

            if (context == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
            }

            tracingService.Trace("EmailSecurityRole.Execute(), Correlation Id: {0}, Initiating User: {1}",
                context.CorrelationId, context.InitiatingUserId);

            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            try
            {
                Email email = (Email)service.Retrieve(EmailToSend.Get(executionContext).LogicalName, EmailToSend.Get(executionContext).Id, new ColumnSet(true));
                //Role role = (Role)service.Retrieve(RecipientRole.Get(executionContext).LogicalName, RecipientRole.Get(executionContext).Id, new ColumnSet(true));

                toList = new List<ActivityParty>();

                //Add any pre-defined recipients specified to the array
                foreach (ActivityParty a in email.To)
                {
                    toList.Add(a);
                }

                //EntityCollection users = GetRoleUsers(service, role.Id);
                Guid roleId = IsGuid(RecipientRole.Get(executionContext));
                if (roleId == Guid.Empty)
                {
                    tracingService.Trace("Invalid Role GUID");
                    throw new InvalidWorkflowException("Invalid Role GUID");
                }

                EntityCollection users = GetRoleUsers(service, roleId);

                ProcessUsers(users);

                //Update the email
                email.To = toList.ToArray();
                service.Update(email);

                //Send
                if (SendEmail.Get(executionContext))
                {
                    SendEmailRequest request = new SendEmailRequest
                    {
                        EmailId = email.Id,
                        TrackingToken = string.Empty,
                        IssueSend = true
                    };

                    service.Execute(request);
                }
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                tracingService.Trace("Exception: {0}", e.ToString());
                throw;
            }

            tracingService.Trace("Exiting EmailSecurityRole.Execute(), Correlation Id: {0}", context.CorrelationId);
        }

        private Guid IsGuid(string value)
        {
            Guid parsed;
            if (Guid.TryParse(value, out parsed))
                return parsed;
            else
                return Guid.Empty;
        }

        private void ProcessUsers(EntityCollection users)
        {
            //Add the users to the array
            foreach (Entity e in users.Entities)
            {
                SystemUser u = (SystemUser)e;
                if (!string.IsNullOrEmpty(u.InternalEMailAddress))
                {
                    ActivityParty party = new ActivityParty
                    {
                        PartyId = new EntityReference(SystemUser.EntityLogicalName, u.SystemUserId.Value)
                    };

                    toList.Add(party);
                }
            }
        }

        private EntityCollection GetRoleUsers(IOrganizationService service, Guid id)
        {
            //Query for the users with security role
            QueryExpression query = new QueryExpression()
            {
                EntityName = SystemUser.EntityLogicalName,
                ColumnSet = new ColumnSet("internalemailaddress", "systemuserid"),
                LinkEntities = 
                    {
                        new LinkEntity
                        {
                            LinkFromEntityName = SystemUser.EntityLogicalName,
                            LinkFromAttributeName = "systemuserid",
                            LinkToEntityName = SystemUserRoles.EntityLogicalName,
                            LinkToAttributeName = "systemuserid",
                            LinkCriteria = new FilterExpression
                            {
                                FilterOperator = LogicalOperator.And,
                                Conditions = 
                                {
                                    new ConditionExpression
                                    {
                                        AttributeName = "roleid",
                                        Operator = ConditionOperator.Equal,
                                        Values = { id }
                                    }
                                }
                            }
                        }
                    }
            };

            return service.RetrieveMultiple(query);
        }

        [RequiredArgument]
        [Input("Email To Send")]
        [ReferenceTarget("email")]
        public InArgument<EntityReference> EmailToSend { get; set; }

        //[RequiredArgument]
        //[Input("Security Role")]
        //[ReferenceTarget("role")]
        //public InArgument<EntityReference> RecipientRole { get; set; }
        [RequiredArgument]
        [Input("Security Role GUID")]
        [ReferenceTarget("role")]
        public InArgument<string> RecipientRole { get; set; }

        [RequiredArgument]
        [Input("Send The Email?")]
        public InArgument<bool> SendEmail { get; set; }
    }
}
